package jcataloger.util;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * This class provides basic zip compression utilities.
 * @author sekobey
 *
 */
public class ZipUtils {

	private ArrayList<File> content;
	
	/**
	 * initialize an array list by using given content
	 * @param content shows the elements which are written to a zip file
	 */
	public ZipUtils(ArrayList<File> content) {
		this.content = content;
	}
	
	/**
	 * default constructor
	 *
	 */
	public ZipUtils() {
		content = new ArrayList<File>();
	}
	
	/**
	 * Add new file to your content.Calls other method.
	 * @param file 
	 */
	public void add(String file) {
		File f = new File(file);
		add(f);
	}
	
	/**
	 * Add new file to your content.
	 * @param file
	 */
	public void add(File file) {
		content.add(file);
	}
	
	/**
	 * compress the content to a zip file, calls other method.
	 * @param zipFile the zip file parameter that the content will be added.
	 */
	public void zipToFile(String zipFile) {
		File f = new File(zipFile);
		zipToFile(f);
	}
	
	/**
	 * compress the content to a zip file
	 * @param zipFile the zip file parameter that the content will be added.
	 */
	public void zipToFile(File zipFile) {
		
	    ZipOutputStream zos = null;
		try {
			// create a ZipOutputStream to zip the data to
			zos = new ZipOutputStream(new FileOutputStream(zipFile));
			// iterate over content for files and folders to zip them seperately
			for (File file : content) {
		    	if ( file.isDirectory() ) {
		    		ZipEntry entry = new ZipEntry(file.getPath()+"/");
		    		zos.putNextEntry(entry);
		    		zipDir(file, zos);
		    	}
		    	else {
		    		zipFile(file, zos);
		    	}
		    }
			// close the stream 
		    zos.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	// add one file to the zip archive
	private void zipFile(File fileToZip, ZipOutputStream zos) {
		byte[] readBuffer = new byte[2156]; 
		int bytesIn = 0;
		
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(fileToZip);
			// create a new zip entry 
			ZipEntry anEntry = new ZipEntry(fileToZip.getPath()); 
			//place the zip entry in the ZipOutputStream object 
			zos.putNextEntry(anEntry); 
			//now write the content of the file to the ZipOutputStream 
			while((bytesIn = fis.read(readBuffer)) != -1) 
			{ 
				zos.write(readBuffer, 0, bytesIn); 
			} 
			//close the Stream
			fis.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		 
	}
	
	// add one directory to the zip archive
	private void zipDir(File dirTozip, ZipOutputStream zos) 
	{ 
		try 
		{ 
			//create a new File object based on the directory we have to zip File    
			if ( !dirTozip.isDirectory() ) {
				throw new IllegalArgumentException(dirTozip+" must be a directory!");
			}
			//get a listing of the directory content 
			String[] dirList = dirTozip.list(); 
			/*byte[] readBuffer = new byte[2156]; 
			int bytesIn = 0;*/ 
			//loop through dirList, and zip the files 
			for(int i=0; i<dirList.length; i++) 
			{ 
				File f = new File(dirTozip, dirList[i]); 
				if(f.isDirectory()) 
				{ 
					//if the File object is a directory, call this 
					//function again to add its content recursively 
					//String filePath = f.getPath();
					// add a directory entry
					ZipEntry anEntry = new ZipEntry(f.getPath()+File.separator);
					zos.putNextEntry(anEntry);
					zipDir(f, zos); 
					//loop again 
					continue; 
				} 
				//if we reached here, the File object f was not a directory 
				//create a FileInputStream on top of f 
				zipFile(f, zos); 
			} 
		} catch(Exception e) 
		{ 
			e.printStackTrace();
		}
	}
	
	/**
	 * unzips a zip archive.Calls unzip(File) method
	 * @param file archive file to be zipped
	 */
	public static void unzip(String file) {
		File f = new File(file);
		unzip(f);
	}
	
	/**
	 * unzips a zip archive
	 * @param file archive file to be unzipped
	 */
	public static void unzip(File file) {
		Enumeration entries;
		ZipFile zipFile;

		try {
			zipFile = new ZipFile(file);

			entries = zipFile.entries();

			byte[] buffer = new byte[1024];
			int len;
			
			while(entries.hasMoreElements()) {
				ZipEntry entry = (ZipEntry)entries.nextElement();

				if(entry.isDirectory()) {
					// Assume directories are stored parents first then children.
					Debug.println("Extracting directory: " + entry.getName());
					// This is not robust, just for demonstration purposes.
					(new File(entry.getName())).mkdir();
					continue;
				}

				Debug.println("Extracting file: " + entry.getName());
				InputStream in = zipFile.getInputStream(entry);
				BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(entry.getName()));
				
				while((len = in.read(buffer)) >= 0)
					out.write(buffer, 0, len);

				in.close();
				out.close();
				
			}

			zipFile.close();
		} catch (IOException ioe) {
			ioe.printStackTrace();
			return;
		}
	}

	/*
	public static final void main(String[] args) {
		try 
		{ 
			
			ZipUtils zip = new ZipUtils();
			
			// zip.add( new File("text") );
			// zip.add( new File("A") );
			// zip.zipToFile(new File("my.zip"));
			
			
			ZipUtils.unzip(new File("my.zip"));
		} 
		catch(Exception e) 
		{ 
		    //handle exception 
		} 
	}

	*/
}